package com.cloud.admin.controller;

import cn.hutool.core.util.StrUtil;
import com.cloud.admin.vo.TokenVo;
import com.cloud.admin.beans.qo.LoginRequest;
import com.cloud.common.base.Result;
import com.cloud.common.enums.ResultEnum;
import com.cloud.common.var.RedisKeys;
import com.cloud.common.var.StaticVar;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.URI;
import java.util.Map;

/**
 * 登录
 * @author Aijm
 * @since 2019/5/17
 */
@Slf4j
@RestController
@RequestMapping("/")
public class LoginController {

    @Value("${auth.clientId}")
    String clientId;

    @Value("${auth.clientSecret}")
    String clientSecret;

    @Value("${auth.grantType}")
    String grantType;

    @Autowired
    LoadBalancerClient loadBalancerClient;


    /*
    存在重复的代码 下面两行
     */
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;
    @Bean
    public TokenStore tokenStore() {
        //第二加载
        RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
        tokenStore.setPrefix(RedisKeys.REDIS_TOKEN_KEY );
        return tokenStore;
    }
    @Autowired
    RestTemplate restTemplate;

    @PostMapping("/userLogin")
    public Result<TokenVo> login(@RequestBody LoginRequest loginRequest) {
        if(loginRequest == null || StringUtils.isEmpty(loginRequest.getUsername())){
            return Result.error(ResultEnum.LOGIN_NAME_NULL);
        }
        if(loginRequest == null || StringUtils.isEmpty(loginRequest.getPassword())){
            return Result.error(ResultEnum.LOGIN_PASSWORD_NULL);
        }
        //账号
        String loginName = loginRequest.getUsername();
        // 密码
        String password = loginRequest.getPassword();
        //申请令牌
        //将令牌存储到redis中
        Map map = applyToken(loginName, password);
        TokenVo build = TokenVo.builder().token(map).build();
        return Result.success(build);
    }

    /**
     * 退出token
     *
     * @param authHeader Authorization
     */
    @DeleteMapping("/logout")
    public Result logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader) {
        if (StrUtil.isBlank(authHeader)) {
            return Result.error("退出失败，token 为空");
        }
        TokenStore tokenStore = tokenStore();
        String tokenValue = authHeader.replace(OAuth2AccessToken.BEARER_TYPE, StrUtil.EMPTY).trim();
        OAuth2AccessToken accessToken = tokenStore().readAccessToken(tokenValue);
        if (accessToken == null || StrUtil.isBlank(accessToken.getValue())) {
            return Result.error("退出失败，token 无效");
        }

        // 清空access token
        tokenStore.removeAccessToken(accessToken);
        // 清空 refresh token
        OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
        tokenStore.removeRefreshToken(refreshToken);
        return Result.success(Boolean.TRUE);
    }

    /**
     * 申请令牌
     *  从nacos中获取认证服务的地址（因为spring security在认证服务中）
     *             从nacos中获取认证服务的一个实例的地址
     *             获取到实例   ServiceInstance serviceInstance = loadBalancerClient.choose(CloudServiceList.CLOUD_AUTH);
     *             此地址就是http://ip:port
     *             URI uri = serviceInstance.getUri(); uri 就喂ip地址和端口
     *             http://localhost:8080/auth/oauth/token  这样报错不能用ip地址 令牌申请的地址
     *             定义header
     * @param username 用户名
     * @param password 密码
     */
    private Map applyToken(String username, String password){
        LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
        String httpBasic = getHttpBasic(clientId, clientSecret);
        header.add("Authorization",httpBasic);

        //定义body
        LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", grantType);
        body.add("username", username);
        body.add("password", password);

        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, header);
        // String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables
        // 设置restTemplate远程调用时候，对400和401不让报错，正确返回数据

        ResponseEntity<Map> exchange = restTemplate.exchange(StaticVar.LOGIN_URL, HttpMethod.POST, httpEntity, Map.class);
        //申请令牌信息
        return exchange.getBody();
    }


    /**
     * 获取httpbasic的串
     * @param clientId
     * @param clientSecret
     * @return
     */
    private String getHttpBasic(String clientId,String clientSecret){
        String string = clientId+":"+clientSecret;
        // 将串进行base64编码
        byte[] encode = Base64Utils.encode(string.getBytes());
        return "Basic "+new String(encode);
    }
}

